namespace NSpring.Logging.XML {

using System;
using System.Text;

/// <summary>
/// Defines preferences for use in formatting objects as XML
/// </summary>
public sealed class XMLFormatOptions {
    private static char[] SPACE_ARRAY                   = new char[] {' '};
    private const string BLANK_STRING                   = "";
    private const string HYPHEN                         = "-";
    private const string SPACE                          = " ";
    private const string UNDERSCORE                     = "_";

    private Case elementCase;
    private Separator elementSeparator;
    private Case attributeCase;
    private Separator attributeSeparator;
    private bool isIndentationEnabled;
    private int indentationWidth                        = 3;
    private bool isVersionLineEnabled                   = false;
    
    /// <summary>
    /// Constructs a new instance, using the default case option of Case.Lower
    /// and separator Separator.Underscore for both elements and attributes
    /// </summary>
    public XMLFormatOptions() {
        elementCase = NSpring.Logging.XML.Case.HeadsDownCamel;
        elementSeparator = NSpring.Logging.XML.Separator.None;
        attributeCase = NSpring.Logging.XML.Case.HeadsDownCamel;
        attributeSeparator = NSpring.Logging.XML.Separator.None;
        isIndentationEnabled = false;
    }
    
    /// <summary>
    /// Constructs a new instance, using the specified case and separator for both elements and attributes
    /// </summary>
    /// <param name="_case">The case to use in constructing element and attribute names</param>
    /// <param name="_separator">The separator to use in constructing element and attribute names</param>
    public XMLFormatOptions(Case _case, Separator _separator) {
        elementCase = _case;
        attributeCase = _case;
        elementSeparator = _separator;
        attributeSeparator = _separator;
        isIndentationEnabled = false;
    }
    
    /// <summary>
    /// Constructs a new instance, using the specified case and separator for both elements and attributes,
    /// as well as the specified indentation (pretty-printing) strategy
    /// </summary>
    /// <param name="_case">The case to use in constructing element and attribute names</param>
    /// <param name="_separator">The separator to use in constructing element and attribute names</param>
    /// <param name="_isIndentationEnabled">Whether or not to indent (pretty-print) formatted XML</param>
    public XMLFormatOptions(Case _case, Separator _separator, bool _isIndentationEnabled) {
        elementCase = _case;
        attributeCase = _case;
        elementSeparator = _separator;
        attributeSeparator = _separator;
        isIndentationEnabled = _isIndentationEnabled;
    }
    
    /// <summary>
    /// Constructs a new instance, using the specified case and separator options
    /// </summary>
    /// <param name="_elementCase">The case to use in constructing element names</param>
    /// <param name="_elementSeparator">The separator to use in constructing element names</param>
    /// <param name="_attributeCase">The case to use in constructing attribute names</param>
    /// <param name="_attributeSeparator">The separator to use in constructing attribute names</param>
    public XMLFormatOptions(Case _elementCase, Separator _elementSeparator, Case _attributeCase, Separator _attributeSeparator) {
        elementCase = _elementCase;
        elementSeparator = _elementSeparator;
        attributeCase = _attributeCase;
        attributeSeparator = _attributeSeparator;
        isIndentationEnabled = false;
    }
    
    /// <summary>
    /// Constructs a new instance, using the specified case and separator options,
    /// as well as the specified indentation (pretty-printing) strategy
    /// </summary>
    /// <param name="_elementCase">The case to use in constructing element names</param>
    /// <param name="_elementSeparator">The separator to use in constructing element names</param>
    /// <param name="_attributeCase">The case to use in constructing attribute names</param>
    /// <param name="_attributeSeparator">The separator to use in constructing attribute names</param>
    /// <param name="_isIndentationEnabled">Whether or not to indent (pretty-print)</param>
    public XMLFormatOptions(Case _elementCase, Separator _elementSeparator, Case _attributeCase, Separator _attributeSeparator, bool _isIndentationEnabled) {
        elementCase = _elementCase;
        elementSeparator = _elementSeparator;
        attributeCase = _attributeCase;
        attributeSeparator = _attributeSeparator;
        isIndentationEnabled = _isIndentationEnabled;
    }
    
    /// <summary>
    /// Constructs a copy of this instance
    /// </summary>
    /// <returns>A new XMLFormatOptions object with the same data as this instance</returns>
    public XMLFormatOptions Copy() {
        XMLFormatOptions copy = new XMLFormatOptions();
        copy.elementCase = this.elementCase;
        copy.elementSeparator = this.elementSeparator;
        copy.attributeCase = this.attributeCase;
        copy.attributeSeparator = this.attributeSeparator;
        copy.isIndentationEnabled = this.isIndentationEnabled;
        copy.indentationWidth = this.indentationWidth;
        copy.isVersionLineEnabled = this.isVersionLineEnabled;
        return copy;
    }
    
    /// <summary>
    /// A convenience property that sets the casing option for both element and attribute names at once
    /// </summary>
    public Case Case {
        set {
            elementCase = value;
            attributeCase = value;
        }
    }
    
    /// <summary>
    /// A convenience property that sets the casing option for both element and attribute names at once
    /// </summary>
    public Separator Separator {
        set {
            elementSeparator = value;
            attributeSeparator = value;
        }
    }
    
    /// <summary>
    /// Gets and sets the casing option for element names.  Defaults to XMLCasingOption.ALL_LOWER_CASE
    /// </summary>
    public Case ElementCase {
        get {
            return elementCase;
        }
        set {
            elementCase = value;
        }
    }
    
    /// <summary>
    /// Gets and sets the separator option for element names.  Defaults to Separator.Underscore
    /// </summary>
    public Separator ElementSeparator {
        get {
            return elementSeparator;
        }
        set {
            elementSeparator = value;
        }
    }
    
    /// <summary>
    /// Gets and sets the casing option for attribute names.  Defaults to XMLCasingOption.ALL_LOWER_CASE
    /// </summary>
    public Case AttributeCase {
        get {
            return attributeCase;
        }
        set {
            attributeCase = value;
        }
    }
    
    /// <summary>
    /// Gets and sets the separator option for attribute names.  Defaults to Separator.Underscore
    /// </summary>
    public Separator AttributeSeparator {
        get {
            return attributeSeparator;
        }
        set {
            attributeSeparator = value;
        }
    }
    
    /// <summary>
    /// Gets and sets the preference for indentation, also called pretty printing.
    /// Defaults to false
    /// </summary>
    public bool IsIndentationEnabled {
        get {
            return isIndentationEnabled;
        }
        set {
            isIndentationEnabled = value;
        }
    }
    
    public int IndentationWidth {
        get {
            return indentationWidth;
        }
        set {
            indentationWidth = Math.Max(1, value);
        }
    }
    
    public bool IsVersionLineEnabled {
        get {
            return isVersionLineEnabled;
        }
        set {
            isVersionLineEnabled = value;
        }
    }
    
    /// <summary>
    /// Constructs an attribute name (using the specified input string) that conforms to the 
    /// casing and separator preferences represented by this instance.  The specified string
    /// should contain spaces where separators are desired in the resulting name; acronyms
    /// should be represented in upper case, all other characters in lower case
    /// </summary>
    /// <param name="name">The input to use when constructing the name</param>
    /// <returns>An element name conforming to the preferences represented by this instance</returns>
    public string GetFormattedAttributeName(string name) {
        string separator;
        if (attributeSeparator == NSpring.Logging.XML.Separator.Hyphen) {
            separator = HYPHEN;
        }
        else if (attributeSeparator == NSpring.Logging.XML.Separator.Underscore) {
            separator = UNDERSCORE;
        }
        else { // if (options.AttributeSeparator == Separator.None)
            separator = BLANK_STRING;
        }

        if (attributeCase == NSpring.Logging.XML.Case.Lower) {
            return String.Join(separator, name.ToLower().Split(SPACE_ARRAY));
        }
        else if (attributeCase == NSpring.Logging.XML.Case.Upper) {
            return String.Join(separator, name.ToUpper().Split(SPACE_ARRAY));
        }
        else if (attributeCase == NSpring.Logging.XML.Case.HeadsDownCamel) {
            string[] words = name.Split(SPACE_ARRAY);
            string word;
            int x, y;
            StringBuilder sb = new StringBuilder(64);
            if (words.Length > 0) {
                sb.Append(words[0].ToLower());
                for(x = 1; x < words.Length; x++) {
                    sb.Append(separator);
                    word = words[x];
                    if (word.Length > 0) {
                        sb.Append(Char.ToUpper(word[0]));
                        for(y = 1; y < word.Length; y++) {
                            sb.Append(word[y]);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        else { // if (options.AttributeCase == Case.HeadsUpCamel)
            string[] words = name.Split(SPACE_ARRAY);
            string word;
            int x, y;
            StringBuilder sb = new StringBuilder(64);
            if (words.Length > 0) {
                word = words[0];
                if (word.Length > 0) {
                    sb.Append(Char.ToUpper(word[0]));
                    for(y = 1; y < word.Length; y++) {
                        sb.Append(word[y]);
                    }
                }
                for(x = 1; x < words.Length; x++) {
                    sb.Append(separator);
                    word = words[x];
                    if (word.Length > 0) {
                        sb.Append(Char.ToUpper(word[0]));
                        for(y = 1; y < word.Length; y++) {
                            sb.Append(word[y]);
                        }
                    }
                }
            }
            return sb.ToString();
        }
    }
    
    /// <summary>
    /// Constructs an element name (using the specified input string) that conforms to the 
    /// casing and separator preferences represented by this instance.  The specified string
    /// should contain spaces where separators are desired in the resulting name; acronyms
    /// should be represented in upper case, all other characters in lower case
    /// </summary>
    /// <param name="name">The input to use when constructing the name</param>
    /// <returns>An element name conforming to the preferences represented by this instance</returns>
    public string GetFormattedElementName(string name) {
        string separator;
        if (elementSeparator == NSpring.Logging.XML.Separator.Hyphen) {
            separator = HYPHEN;
        }
        else if (elementSeparator == NSpring.Logging.XML.Separator.Underscore) {
            separator = UNDERSCORE;
        }
        else { // if (options.ElementSeparator == Separator.None)
            separator = BLANK_STRING;
        }
        
        if (elementCase == NSpring.Logging.XML.Case.Lower) {
            return String.Join(separator, name.ToLower().Split(SPACE_ARRAY));
        }
        else if (elementCase == NSpring.Logging.XML.Case.Upper) {
            return String.Join(separator, name.ToUpper().Split(SPACE_ARRAY));
        }
        else if (elementCase == NSpring.Logging.XML.Case.HeadsDownCamel) {
            string[] words = name.Split(SPACE_ARRAY);
            string word;
            int x, y;
            StringBuilder sb = new StringBuilder(64);
            if (words.Length > 0) {
                sb.Append(words[0].ToLower());
                for(x = 1; x < words.Length; x++) {
                    sb.Append(separator);
                    word = words[x];
                    if (word.Length > 0) {
                        sb.Append(Char.ToUpper(word[0]));
                        for(y = 1; y < word.Length; y++) {
                            sb.Append(word[y]);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        else { // if (options.ElementCase == Case.HeadsUpCamel)
            string[] words = name.Split(SPACE_ARRAY);
            string word;
            int x, y;
            StringBuilder sb = new StringBuilder(64);
            if (words.Length > 0) {
                word = words[0];
                if (word.Length > 0) {
                    sb.Append(Char.ToUpper(word[0]));
                    for(y = 1; y < word.Length; y++) {
                        sb.Append(word[y]);
                    }
                }
                for(x = 1; x < words.Length; x++) {
                    sb.Append(separator);
                    word = words[x];
                    if (word.Length > 0) {
                        sb.Append(Char.ToUpper(word[0]));
                        for(y = 1; y < word.Length; y++) {
                            sb.Append(word[y]);
                        }
                    }
                }
            }
            return sb.ToString();
        }
    }
    
}

}
